package com.cloudft.http.services;


import android.app.Activity;
import android.app.ActivityGroup;
import android.content.Context;
import android.net.ConnectivityManager;
import android.os.AsyncTask;
import android.util.Log;

import com.cloudft.http.config.IResponseListener;
import com.cloudft.http.models.CFHttpException;
import com.cloudft.http.models.HTTPRequest;
import com.cloudft.http.models.HTTPResponse;

/**
 * APIService class - This class is responsible for calling the CacheService/HTTPService
 * synchronously or asynchronously depending on the request parameters 
 */

public class APIService {
	public static final String TAG = "CF-HTTP-APIService";
	private APIAsyncTask apiAsyncTask = null;

	/**
	 * "getAsyncRequest()" method helps in making asynchronous call for fetching the data
	 * @param HTTPRequest - Request object created by the calling service/activity
	 * @param Context - Context object passed by the calling service/activity
	 * @param IResponseListener - IResponseListener object for getting the callback in the calling service/activity
	 */
	public void getAsyncRequest(HTTPRequest request, Context context, IResponseListener dataListener){
		apiAsyncTask = new APIAsyncTask(request, context, dataListener);
		apiAsyncTask.execute();
	}

	/**
	 * "getSyncRequest()" method helps in making synchronous call for fetching the data
	 * @param HTTPRequest - Request object created by the calling service/activity
	 * @param Context - Context object passed by the calling service/activity
	 * @throws CFHttpException 
	 */
	public HTTPResponse getSyncRequest(HTTPRequest request, Context context) throws CFHttpException{
		return getResponse(request,context);
	}

	/**
	 * AsyncTask for getting fetching the data in the asynchronous manner
	 */
	public class APIAsyncTask extends AsyncTask<Void, Boolean, Object> {

		HTTPResponse		response;
		HTTPRequest  		request;
		Context 			context;
		IResponseListener	dataListener;

		/**
		 * APIAsyncTask Constructor - Initializes the APIAsynctask by passing the request and other objects
		 * @param HTTPRequest - Request object created by the calling service/activity
		 * @param Context - Context object passed by the calling service/activity
		 * @param IResponseListener - IResponseListener object for getting the callback in the calling service/activity
		 */
		public APIAsyncTask(HTTPRequest request, Context context, IResponseListener	dataListener){
			this.request= request;
			this.context = context;
			this.dataListener = dataListener;
		}

		@Override
		protected Object doInBackground(Void... arg0) {
			Object response = null;
			try {
				if(apiAsyncTask.isCancelled()) {
					return null;
				} else {
					response= getResponse(this.request, this.context);
					return response;
				}
			} catch(CFHttpException ex) {
				ex.printStackTrace();
				response = ex;
			} catch(NullPointerException e) {
				e.printStackTrace();
			} catch(Exception e) {
				Log.i("APIService Exception :", e.getMessage());
			}

			//NOTE: Control doesn't go to the onPostExecute() method if called from non-activity class
			if(!apiAsyncTask.isCancelled() && !(this.context instanceof Activity)  && !(this.context instanceof ActivityGroup) ){
				callBack(response);
			}

			return response;
		}

		@Override
		protected void onPostExecute(Object result) {
			callBack(result);
		}


		/**
		 * CallBack method - Helps in passing the HTTPResponse object from APIService to the 
		 * calling service/method 
		 */
		private void callBack(Object response){
			if(response != null && response instanceof HTTPResponse){
				if(!apiAsyncTask.isCancelled()) {
					this.dataListener.onSuccess((HTTPResponse) response);
				}
			} else if(response != null && response instanceof CFHttpException) {
				if(!apiAsyncTask.isCancelled()) {
					this.dataListener.onFailure((CFHttpException) response);
				}
			} 
		}
		
	}
	
	/**
	 * Method to cancel the Asynchronous task 
	 */
	public void cancelAsyncTask() {
		if(apiAsyncTask != null && !apiAsyncTask.isCancelled()) {
			apiAsyncTask.cancel(true);
		}
	}

	/**
	 * Method to check whether the asynchronous task is cancelled 
	 */
	public boolean isAsyncTaskCancelled() {
		if(apiAsyncTask != null) {
			return apiAsyncTask.isCancelled();
		}
		return false;
	}
	
	/**
	 * getResponse() method- This is the private method of the APIService that calls the
	 * CacheService/HTTPService depending on the value of isCacheable flag inside the 
	 * HTTPRequest object
	 * @param HTTPRequest - Request object created by the calling service/activity
	 * @param Context - Context object passed by the calling service/activity
	 * @throws CFHttpException 
	 */
	private HTTPResponse getResponse(HTTPRequest request,Context context) throws CFHttpException{

		HTTPResponse response = null;
		
		//Check if caching is enabled
		// we also fetch response from cache, when there is no network
		// but we skip cache when request is POST or HTTPS
		if(request != null){
			if( ( !isNetworkAvailable(context) || request.getIsCacheable())
					
							&& !request.getRequestType().trim().equalsIgnoreCase("post")
							&& !request.getUrl().trim().toLowerCase().startsWith("https:")) {

				CacheService cachingService = new CacheService(context);
				response = cachingService.fetchCachedData(request,context);
			} else {
				HTTPService httpService = new HTTPService();
				response = httpService.fetchServerData(request);
			}
		}

		Log.d(TAG, "Response came from " + (response.isFromCache() ? "cache" : "server") + "  URL: " + request.getUrl());
		
		return response;	
	}

	/**
	 * Returns true if Internet Connection is available and connected, false otherwise.
	 * @param context
	 * @return
	 */
	public static boolean isNetworkAvailable(Context context) {
		ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
	    return (cm.getActiveNetworkInfo() != null
	            && cm.getActiveNetworkInfo().isAvailable()
	            && cm.getActiveNetworkInfo().isConnected());
	}

}
